#ifndef __INC_QGMTRL_H__
#define __INC_QGMTRL_H__

#ifdef _MSC_VER
#pragma once
#endif

#include <float.h>

//////////////////////////////////////////////////////////////////////////
// texture

// type
enum qgTextureType
{
	QGTEXTURE_1D,
	QGTEXTURE_2D,
	QGTEXTURE_3D,
	QGTEXTURE_CUBE,
};

// face
enum qgTextureFace
{
	QGTXFX_NONE,
	QGTXFX_PX,
	QGTXFX_NX,
	QGTXFX_PY,
	QGTXFX_NY,
	QGTXFX_PZ,
	QGTXFX_NZ,
};

// flag
#define QGTEXF_NOMIP						0x0100			// do not use mipmap
#define QGTEXF_IMAGE						0x0200			// keep resident image
#define QGTEXF_MOTION						0x1000			// motion / internal use only
#define QGTEXF_PRELOAD						0x2000			// preload for motion
#define QGTEXF_OFF							0x8000			// off-screen(=render target) texture

#define QGTEXF_S2D							(QGTEXF_NOMIP)
#define QGTEXF_RENDERTARGET					(QGTEXF_IMAGE | QGTEXF_OFF)

// inst
#define QGTEXI_AUTOMIP						0x0001			// use auto mipmap
#define QGTEXI_CLAMP						0x0008			// clamp maximum size of device
#define QGTEXI_ADJPOW2						0x0010			// adjust power of 2 size
#define QGTEXI_ADJSQR						0x0020			// adjust square size
#define QGTEXI_LOCKOFF						0x0100			// off lock
#define QGTEXI_MOTION						0x1000			// texture is for motion
#define QGTEXI_LIST							0x2000			// texutre is for motion list
#define QGTEXI_OFF							0x8000			// texture is for off

// map
struct qgTextureMap
{
	kbyte*				data;
	kint				pitch;
	kint				slice;

	kint				width;
	kint				height;
	kint				depth;
};

// motion interface
struct qgITextureMotion
{
	virtual kint GetCount() = 0;
	virtual kint GetFps() = 0;
	virtual kint GetIndex() = 0;
	virtual float GetLength() = 0;
	virtual float GetAdvance() = 0;
	virtual bool IsLoop() = 0;
	virtual bool IsPlay() = 0;

	virtual void Reset() = 0;
	virtual void SetFixedLength(float len) = 0;
	virtual void SetFps(kint v) = 0;
	virtual void SetFrameRange(kint min, kint max) = 0;
	virtual void SetPosition(float pos) = 0;
	virtual void SetPlayMode(bool isplay) = 0;
	virtual void TurnLoop(bool isloop) = 0;

	//
	virtual bool ManualUpdate(float adv, bool& res, kuintptr& ptr) = 0;

	//
	virtual bool MotionUpdate(float adv) = 0;
	virtual qgImage* MotionImage(kint index = -1) = 0;
};

// render texture interface
struct qgITextureOff
{
	virtual bool EnterOff(kint clearance = 0xFF, const tpl::ColorValue& color = tpl::ColorValues::Black) = 0;
	virtual bool LeaveOff() = 0;
};

// texture, abstract
class KAPI qgTexture : public qgObj
{
	KCH_SUBCLASS(qgTexture, qgObj);
	KT_DECL_TYPE_INIT(qgTexture);

public:
	typedef char* KeyType;

protected:
	KeyType				_key;

	kuint				_flags : 16;
	kuint				_insts : 16;

	qgTextureType		_textype : 8;
	kuint				_mips : 8;
	kuint				_width : 16;
	kuint				_height : 16;
	kuint				_depth : 16;

	kbool				_alpha;
	qgColorFormat		_fmt;

	kint				_lock_mip : 16;
	qgTextureFace		_lock_face : 16;

protected:
	qgTexture();
	~qgTexture();

public:
	virtual bool EnterMap(qgTextureMap* map, kint flag, kint mips = 0, qgTextureFace face = QGTXFX_NONE) = 0;
	virtual bool LeaveMap() = 0;

	// for motion
	virtual qgITextureMotion* GetMotion() { return NULL; }

	// for off
	virtual qgITextureOff* GetOff() { return NULL; }

	//
	qgTextureType GetTexType() { return _textype; }
	kint GetWidth() { return _width; }
	kint GetHeight() { return _height; }
	kint GetDepth() { return _depth; }
	kint GetMipCount() { return _mips; }
	bool HasAlpha() { return _alpha; }
	bool HasMips() { return !K_OFLAG(_flags, QGTEXF_NOMIP); }
	bool IsMotion() { return K_OFLAG(_insts, QGTEXI_MOTION); }
	bool IsOff() { return K_OFLAG(_insts, QGTEXI_OFF); }
	qgColorFormat GetColorFormat() { return _fmt; }
	KeyType GetKey() { return _key; }
};


//////////////////////////////////////////////////////////////////////////
// depth, abstract
class KAPI qgDepthBuffer : public qgObj
{
	KCH_SUBCLASS(qgDepthBuffer, qgObj);

protected:
	kuint				_width : 16;
	kuint				_height : 16;

protected:
	qgDepthBuffer() {};
	~qgDepthBuffer() {};

public:
	kuint GetWidth() { return _width; }
	kuint GetHeight() { return _height; }
};


//////////////////////////////////////////////////////////////////////////
// rasterize

// data
struct qgRaszData
{
	qgFill				fill : 8;
	qgCull				cull : 8;

	qgCmpOp				dfunc : 8;
	bool				dwrite : 8;
	kint				dbias;
	float				dclamp;

	float				slope_scale;
};

// rasz, abstract
class KAPI qgRasz : public qgObj
{
	KCH_SUBCLASS(qgRasz, qgObj);
	KT_DECL_TYPE_INIT(qgRasz);

protected:
	qgRaszData			_data;

protected:
	qgRasz() {};
	~qgRasz() {};

public:
	const qgRaszData& GetData() { return _data; }
};


//////////////////////////////////////////////////////////////////////////
// blend

// state
struct qgBlndState
{
	qgBlendOp			op : 16;
	qgBlendValue		src : 8;
	qgBlendValue		dest : 8;
};

// data
struct qgBlndData
{
	qgMask				mask;
	qgBlndState			blend;
	qgBlndState			alpha;
};

// blnd, abstract
class KAPI qgBlnd : public qgObj
{
	KCH_SUBCLASS(qgBlnd, qgObj);
	KT_DECL_TYPE_INIT(qgBlnd);

protected:
	qgBlndData			_data;

protected:
	qgBlnd() {};
	~qgBlnd() {};

public:
	const qgBlndData& GetData() { return _data; }
};


//////////////////////////////////////////////////////////////////////////
// splr

// data
struct qgSplrData
{
	qgFilter			filt : 8;
	qgAddress			addru : 8;
	qgAddress			addrv : 8;
	qgAddress			addrw : 8;
	qgCmpOp				cop;			// comparison function
	tpl::ColorValue		border;			// border color
	float				lodbias;
	tpl::Vec2			lodrange;
};

// splr, abstract
class KAPI qgSplr : public qgObj
{
	KCH_SUBCLASS(qgSplr, qgObj);
	KT_DECL_TYPE_INIT(qgSplr);

protected:
	qgSplrData			_data;

protected:
	qgSplr() {};
	~qgSplr() {};

public:
	const qgSplrData& GetData() { return _data; }
};


//////////////////////////////////////////////////////////////////////////
// shader
class qgShader;
struct qgShaderVar;
struct qgShaderAutoData;

// type
enum qgShaderType
{
	QGSHADER_PS,
	QGSHADER_VS,
	QGSHADER_GS,			// do not use
	QGSHADER_DS,			// do not use
	QGSHADER_HS,			// do not use
	QGSHADER_CS,			// do not use
	QGSHADER_EF,			// do not use
};

// role
enum qgShaderRole
{
	QGSHDR_MANUAL,
	QGSHDR_AUTO,
	QGSHDR_DISCARD,
	QGSHDR_MAX_VALUE
};

// data
enum qgShaderData
{
	QGSHDD_UNKNOWN,
	QGSHDD_FLOAT1,
	QGSHDD_FLOAT2,
	QGSHDD_FLOAT3,
	QGSHDD_FLOAT4,
	QGSHDD_FLOAT16,
	QGSHDD_INT1,
	QGSHDD_INT2,
	QGSHDD_INT3,
	QGSHDD_INT4,
	QGSHDD_BYTE1,
	QGSHDD_BYTE2,
	QGSHDD_BYTE3,
	QGSHDD_BYTE4,
	QGSHDD_SPLR_1D,
	QGSHDD_SPLR_2D,
	QGSHDD_SPLR_3D,
	QGSHDD_SPLR_CUBE,
	QGSHDD_MAX_VALUE
};

// auto
enum qgShaderAuto
{
	QGSHDA_ORTHO_PROJ,
	QGSHDA_WORLD,
	QGSHDA_VIEW,
	QGSHDA_PROJ,
	QGSHDA_VIEWPROJ,
	QGSHDA_INVVIEW,
	QGSHDA_WORLDVIEWPROJ,
	QGSHDA_TEX0,
	QGSHDA_TEX1,
	QGSHDA_TEX2,
	QGSHDA_TEX3,
	QGSHDA_PROPVEC0,
	QGSHDA_PROPVEC1,
	QGSHDA_PROPVEC2,
	QGSHDA_PROPVEC3,
	QGSHDA_PROPMAT0,
	QGSHDA_PROPMAT1,
	QGSHDA_PROPMAT2,
	QGSHDA_PROPMAT3,
	QGSHDA_MATPALETTE,
	QGSHDA_MAX_VALUE
};

// insts
#define QGSHDI_PS				K_BIT(QGSHADER_PS)
#define QGSHDI_VS				K_BIT(QGSHADER_VS)
#define QGSHDI_GS				K_BIT(QGSHADER_GS)
#define QGSHDI_DS				K_BIT(QGSHADER_DS)
#define QGSHDI_HS				K_BIT(QGSHADER_HS)
#define QGSHDI_CS				K_BIT(QGSHADER_CS)

// flags
#define QGSHDF_BINARY			0x8000

// shader callback
typedef tpl::Function<bool(kint, const qgShaderVar*, qgShader*)> qgShaderIntrFunc;
typedef void(*qgShaderAutoFunc)(qgDevice*, kpointer, const qgShaderVar&); // simple for speed issue. not for user

// var
struct qgShaderVar
{
	ksize_t				hash;
	tpl::BlueStr<32>	name;

	qgShaderRole		role : 8;
	qgShaderData		data : 8;

	kuint				size : 16;
	kuintptr			offset;

	kpointer			intr;			// auto only
	kpointer			ptr;			// D11, DX9 only
};

// auto info
struct qgShaderAutoInfo
{
	ksize_t				hash;
	const char*			name;
	qgShaderAuto		type;
	qgShaderAutoFunc	func;
};

// shader, abstract
class KAPI qgShader : public qgObj
{
	KCH_SUBCLASS(qgShader, qgObj);
	KT_DECL_TYPE_INIT(qgShader);

public:
	typedef tpl::BlueStr<32> KeyType;
	typedef tpl::VecArray<tpl::BlueStr<64> > ConditionArray;

protected:
	KeyType				_key;

	kuint				_insts;

	qgShaderIntrFunc	_cbintr;
	ConditionArray		_conds;

protected:
	qgShader() {};
	~qgShader() {};

public:
	virtual bool BindFile(qgShaderType type, const char* filename, kint flags) = 0;
	virtual bool BindData(qgShaderType type, kconstpointer data, kint size, kint flags) = 0;
	virtual bool BindShader(qgShaderType type, const char* name) = 0;
	virtual bool Link() = 0;

	virtual qgShaderVar* GetVars(kint stage, kint* count) = 0;

	bool HasPixelShader() { return K_OFLAG(_insts, QGSHDI_PS); }
	bool HasVertexShader() { return K_OFLAG(_insts, QGSHDI_VS); }
	bool HasGeometryShader() { return K_OFLAG(_insts, QGSHDI_GS); }
	bool HasDomainShader() { return K_OFLAG(_insts, QGSHDI_DS); }
	bool HasHurlShader() { return K_OFLAG(_insts, QGSHDI_HS); }
	bool HasComputeShader() { return K_OFLAG(_insts, QGSHDI_CS); }

	void AddCondition(const char* str);
	bool HasCondition(const char* str);
	void ClearConditions();

	void SetInterrupt(const qgShaderIntrFunc& func) { _cbintr = func; }
};


//////////////////////////////////////////////////////////////////////////
#include "qgmtrl.inl"

#endif	// __INC_QGMTRL_H__
